#include "SVN_Helicopter.h"

//Nunchuck
WiiChuck chuck = WiiChuck();
int angleStart, currentAngle;
int tillerStart = 0;
double angle;

int _Score = 0;
bool gameStarted = false;
bool _UpdateMap = true;
int _UpdateMapSpeed = 600;
helicopter _Heli;

ISR(TIMER2_OVF_vect)
{
	TCNT2 = tcnt2;

	TicksFrames++;
	TicksUpdateShove++;

	//On 33 miliseconds = 30 Frames a second.
	if (TicksFrames >= _UpdateMapSpeed)
	{
		_UpdateMap = true;
		//Maximum Speed.
		if (_UpdateMapSpeed > 33)
		{
			_UpdateMapSpeed -= 2;
		}
		TicksFrames = 0;
	}
}

void TpCalibration(uint8_t checkEeprom)
{
	int i;
	CAL_POINT lcdPoints[3] = {{CAL_POINT_X1,CAL_POINT_Y1},
							 {CAL_POINT_X2,CAL_POINT_Y2},
							 {CAL_POINT_X3,CAL_POINT_Y3}}; //calibration point postions
	CAL_POINT tpPoints[3];

	//calibration data in EEPROM?
	if(tp.readCalibration(TP_EEPROMADDR) && checkEeprom)
	{
		return;
	}

	//wait for touch release
	lcd.clear(COLOR_WHITE);
	lcd.drawText((lcd.getWidth()/2)-50, (lcd.getHeight()/2)-10, "Calibration", 1, COLOR_BLACK, COLOR_WHITE);
	while(tp.getPressure() > 5)
	{
		tp.service();
	};

	//show calibration points
	i = 0;
	do
	{
		//draw points
		lcd.drawCircle(lcdPoints[i].x, lcdPoints[i].y, 1, RGB(0,0,0));
		lcd.drawCircle(lcdPoints[i].x, lcdPoints[i].y, 4, RGB(0,0,0));
		lcd.drawCircle(lcdPoints[i].x, lcdPoints[i].y, 8, RGB(255,0,0));

		//run service routine
		tp.service();

		//press dectected?
		if(tp.getPressure() > 5)
		{
			tpPoints[i].x = tp.getXraw();
			tpPoints[i].y = tp.getYraw();
			i++;
			lcd.clear(COLOR_WHITE);
			delay(100);
		}
	}
	while(i < 3);

	//calulate calibration matrix
	tp.setCalibration(lcdPoints, tpPoints);

	//save calibration matrix
	tp.writeCalibration(TP_EEPROMADDR);
}

bool growPos;
int growPosLengt = 1;
int growPosSize;

bool growHeight;
int growHeightLengt = 1;
int growHeightSize;

void DrawMap(block * map, int mapStartIndex)
{
	block prevDrawBlock, currentDrawBlock;
	for(int i = 0; i < MAP_WITH; i++)
	{
		int j;
		if (mapStartIndex -1 < 0)
		{
			j = 31;
		}
		else
		{
			j = mapStartIndex -1;
		}

		prevDrawBlock = map[j];
		currentDrawBlock = map[mapStartIndex];

		if (i == MAP_WITH - 1)
		{
			//Position
			growPosLengt--;
			if (growPosLengt == 0)
			{
				growPosLengt = rand() % 10 + 6;
				growPosSize = rand() % 4 + 2;

				if (rand() % 1 == 0)
				{
					growPos = true;
				}
				else
				{
					growPos = false;
				}
			}

			if (growPos)
			{
				currentDrawBlock.pos =  prevDrawBlock.pos + growPosSize;
				if (currentDrawBlock.pos > 80)
				{
					growPos = false;
					growPosLengt = rand() % 10 + 6;
					growPosSize = rand() % 4 + 2;
				}
			}
			else
			{
				currentDrawBlock.pos =  prevDrawBlock.pos - growPosSize;
				if (currentDrawBlock.pos < 10)
				{
					growPos = true;
					growPosLengt = rand() % 10 + 6;
					growPosSize = rand() % 4 + 2;
				}
			}
			//End Position

			//Height
			growHeightLengt--;
			if (growHeightLengt == 0)
			{
				growHeightLengt = rand() % 10 + 6;
				growHeightSize = rand() % 4 + 2;

				if (rand() % 1 == 0)
				{
					growHeight = true;
				}
				else
				{
					growHeight = false;
				}
			}

			if (growHeight)
			{
				currentDrawBlock.height =  prevDrawBlock.height + growHeightSize;
				if (currentDrawBlock.height > 140)
				{
					growHeight = false;
					growHeightLengt = rand() % 10 + 6;
					growHeightSize = rand() % 4 + 2;
				}
			}
			else
			{
				currentDrawBlock.height =  prevDrawBlock.height - growHeightSize;
				if (currentDrawBlock.height < 50)
				{
					growHeight = true;
					growHeightLengt = rand() % 10 + 6;
					growHeightSize = rand() % 4 + 2;
				}
			}
			//End Height

			map[mapStartIndex] = currentDrawBlock;

			lcd.fillRect(i * 10 , 0, (i * 10) + 9 ,  currentDrawBlock.pos - 1, COLOR_GREEN);
			lcd.fillRect(i * 10 , currentDrawBlock.pos, (i * 10) + 9 , (currentDrawBlock.pos + currentDrawBlock.height) - 1, COLOR_BLACK);
			lcd.fillRect(i * 10 , currentDrawBlock.pos + currentDrawBlock.height, (i * 10) + 9 , 240, COLOR_GREEN);
		}
		else
		{
			if ((prevDrawBlock.pos - currentDrawBlock.pos) != 0)
			{
				if ((prevDrawBlock.pos - currentDrawBlock.pos) < 0)
				{
					lcd.fillRect(i * 10 , prevDrawBlock.pos,(i * 10) + 9 ,currentDrawBlock.pos -1 ,COLOR_GREEN);
				}
				else
				{
					lcd.fillRect(i * 10 , currentDrawBlock.pos  ,(i * 10) + 9 , (currentDrawBlock.pos + (prevDrawBlock.pos - currentDrawBlock.pos)) - 1,COLOR_BLACK);
				}
			}
			if ((prevDrawBlock.pos + prevDrawBlock.height ) != (currentDrawBlock.pos + currentDrawBlock.height))
			{
				if ((prevDrawBlock.pos + prevDrawBlock.height ) < (currentDrawBlock.pos + currentDrawBlock.height))
				{

					lcd.fillRect(i * 10 , prevDrawBlock.pos + prevDrawBlock.height  ,(i * 10) + 9 , (currentDrawBlock.pos + currentDrawBlock.height) -1,COLOR_BLACK);
				}
				else
				{
					lcd.fillRect(i * 10 , currentDrawBlock.pos + currentDrawBlock.height ,(i * 10) + 9 , (prevDrawBlock.pos + prevDrawBlock.height) -1,COLOR_GREEN);

				}
			}
		}

		mapStartIndex++;
		if (mapStartIndex >= MAP_WITH)
		{
			mapStartIndex = 0;
		}
	}
}

void InitializeDraw(block * map, MI0283QT2 * lcd)
{
	lcd->clear(COLOR_GREEN);
	for(int i = 0; i < MAP_WITH; i++)
	{
		lcd->fillRect(i * 10 , map[i].pos, (i * 10) + 9 , (map[i].pos + map[i].height) - 1, COLOR_BLACK);
	}
}

void InitializeMap(block * map)
{
	map[0].pos = 5;
	map[0].height = 120;

	map[1].pos = 10;
	map[1].height = 120;

	map[2].pos = 15;
	map[2].height = 120;

	map[3].pos = 20;
	map[3].height = 120;

	map[4].pos = 25;
	map[4].height = 120;

	map[5].pos = 30;
	map[5].height = 120;

	map[6].pos = 35;
	map[6].height = 120;

	map[7].pos = 40;
	map[7].height = 120;

	map[8].pos = 45;
	map[8].height = 120;

	map[9].pos = 50;
	map[9].height = 120;

	map[10].pos = 55;
	map[10].height = 120;

	map[11].pos = 60;
	map[11].height = 80;

	map[12].pos =100;
	map[12].height = 80;

	map[13].pos = 90;
	map[13].height = 80;

	map[14].pos = 80;
	map[14].height = 80;

	map[15].pos = 80;
	map[15].height = 70;

	map[16].pos = 85;
	map[16].height = 70;

	map[17].pos = 80;
	map[17].height = 70;

	map[18].pos = 75;
	map[18].height = 120;

	map[19].pos = 70;
	map[19].height = 120;

	map[20].pos = 65;
	map[20].height = 120;

	map[21].pos = 60;
	map[21].height = 120;

	map[22].pos = 55;
	map[22].height = 120;

	map[23].pos = 50;
	map[23].height = 120;

	map[24].pos = 45;
	map[24].height = 120;

	map[25].pos = 40;
	map[25].height = 120;

	map[26].pos = 35;
	map[26].height = 120;

	map[27].pos = 30;
	map[27].height = 120;

	map[28].pos = 25;
	map[28].height = 120;

	map[29].pos = 20;
	map[29].height = 120;

	map[30].pos = 15;
	map[30].height = 120;

	map[31].pos = 10;
	map[31].height = 120;

}

void ResetHelicopter(helicopter * heli)
{
	heli->x = 20;
	heli->y = 80;
	heli->width = 15;
	heli->height = 15;
	heli->movement = 0;
}

void MoveHelicopter(helicopter * heli)
{
	//store old data
	int oldX = heli->x;
	int oldY = heli->y;

	//move helicopter
	if(!((int)(heli->y + heli->movement) <= 0 || (int)(heli->y+heli->height+heli->movement) > SCREEN_HEIGHT)) {
		heli->y += heli->movement;
	}

	if(oldX!=(int)heli->x || oldY!=(int)heli->y)
	{ //if there is a location change
		if(oldY > heli->y)
		{
			lcd.fillRect((int)heli->x, (int)(heli->y+heli->height), (int)(oldX + heli->width - 1) , (int)(oldY+heli->height), COLOR_BLACK);
		}
		else
		{
			lcd.fillRect((int)oldX, (int)oldY, (int)(heli->x + heli->width - 1) , (int)(heli->y-1), COLOR_BLACK);
		}
		lcd.fillRect((int)heli->x, (int)heli->y, (int)(heli->x + heli->width - 1), (int)(heli->y+heli->height - 1), COLOR_BLUE);
	}
}

void StartGame()
{
	gameStarted = true;
	_ArrayBlockIndex = 0;
	_Score = 0;

	ResetHelicopter(&_Heli);
	InitializeMap(_Map);
	InitializeDraw(_Map, &lcd);
	//begin nunchuck comm
	chuck.begin();
	chuck.update();

	//set timers
	cli();

	/* First disable the timer overflow interrupt while we're configuring */
	TIMSK2 &= ~(1 << TOIE2);

	/* Configure timer2 in normal mode (pure counting, no PWM etc.) */
	TCCR2A &= ~((1 << WGM21) | (1 << WGM20));
	TCCR2B &= ~(1 << WGM22);

	/* Select clock source: internal I/O clock */
	ASSR &= ~(1 << AS2);

	/* Disable Compare Match A interrupt enable (only want overflow) */
	TIMSK2 &= ~(1 << OCIE2A);

	/* Now configure the prescaler to CPU clock divided by 128 */
	TCCR2B |= (1 << CS22)  | (1 << CS20); // Set bits
	TCCR2B &= ~(1 << CS21);             // Clear bit

	/* Finally load end enable the timer */
	TCNT2 = tcnt2;
	TIMSK2 |= (1 << TOIE2);

	sei();
}

void ShowScores(uint16_t score = 0)
{
	//als files niet bestaat, maak nieuwe
	if(files == NULL)
	{
		files = new Files();
	}
	//als score ingevuld, sla score op
	if(score > 0)
	{
		files->SetScore(score, "Player");
	}

	//haal scores op, maximaal lijst van 10
	char * highscores[10];
	files->GetHighscores(highscores);

	int pixelshift = 10;
	lcd.clear(COLOR_BLACK);

	//teken elke score op beeld. of 10 of tot einde lijst
	for(int i = 0; i < 10 && highscores[i] != NULL; i++)
	{
		lcd.drawText(10, pixelshift, highscores[i], 1, COLOR_WHITE,COLOR_BLACK);
		pixelshift = pixelshift + 15;
	}

	//terug naar menu
	touch.buttonsClear();
	touch.buttonAdd( 70,  160, 180, 40, COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, true,  false, 0, 2, "Menu", 1);
	touch.buttonsDraw();

	TouchButtonPtr btn;
	//controleer op touch input
	while(true)
	{
		tp.service();

		btn = touch.check();
		if (btn != NULL)
		{
			switch(btn->caption[0]) {
			case 'M' :
				touch.buttonsClear();
				//ruim files op
				files->~Files();
				gameStarted = false;
				//terug naar menu
				DrawMenu();
				return;
			default:
				return;
			}
		}
	}
}

bool CalculateCollision(block * map, helicopter * heli, int startIndex)
{
	int location = heli->x / 10;
	int pos;
	if ((startIndex + location) >= MAP_WITH )
	{
		pos = (startIndex + location) - MAP_WITH;
	}
	else
	{
		pos = startIndex + location;
	}

	if (heli->y < map[pos].pos)
	{
		return true;
	}
	if (heli->y + heli->height > map[pos].pos + map[pos].height)
	{
		return true;
	}

	return false;
}

void DrawMenu()
{
	//clear screen
	lcd.clear(COLOR_BLACK);

	lcd.drawText(30,30, "HELIGAME", 4, COLOR_GREEN, COLOR_WHITE);
	touch.buttonAdd( 70,  110, 180, 40, COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, true,  false, 0, 2, "Start Game", 1);
	touch.buttonAdd( 70,  160, 180, 40, COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, true,  false, 0, 2, "Highscores", 2);
	touch.buttonsDraw();
}

int main()
{
	Wire.begin();
	init();
	//init Display
	lcd.init(4); //spi-clk = Fcpu/4

	//init Touch-Controller
	tp.init();

	//Touch-Panel Calibration
	tp.service();
	if(tp.getPressure() > 5)
	{
		TpCalibration(0); //dont check EEPROM for calibration data
	}
	else
	{
		TpCalibration(1); //check EEPROM for calibration data
	}

	//Backlight LED
	lcd.led(100);

	DrawMenu();

	while(1)
	{
		if (gameStarted)
		{
			chuck.update();

		    short int yValue = (short int)chuck.readAccelY();

		    if(!(yValue < -200 || yValue > 200))
			    _Heli.movement = yValue / 30;

		    char integer_string[32];
			sprintf(integer_string, "%d", _Score);

			char score_string[64] = "Score: "; // make sure you allocate enough space to append the other string

			strcat(score_string, integer_string);

		    if (CalculateCollision(_Map, &_Heli,_ArrayBlockIndex))
		    {
		    	lcd.clear(COLOR_BLACK);
		    	lcd.drawText(25,40,"DOOD",8,COLOR_BLACK,COLOR_RED);
		    	lcd.drawText(25,150,score_string,3,COLOR_BLACK,COLOR_RED);
		    	delay(2000);
		    	gameStarted = false;
		    	ShowScores(_Score);
		    }
		    else
		    {
				MoveHelicopter(&_Heli);
				lcd.drawText(30, 0, score_string, 2 ,COLOR_BLACK, COLOR_GREEN);

				//Move the map and redraw.
				if (_UpdateMap)
				{
					_Score++;

					DrawMap(_Map, _ArrayBlockIndex);
					_ArrayBlockIndex++;
					if (_ArrayBlockIndex == MAP_WITH)
					{
						_ArrayBlockIndex = 0;
					}
					_UpdateMap = false;
				}
		    }
		}
		else
		{
			static uint16_t last_x = 0;
			TouchButtonPtr btn;

			//service routine
			tp.service();

			btn = touch.check();
			if (btn != NULL)
			{
				switch(btn->caption[0]) {
				case 'S' :
					StartGame();
					break;
				case 'H' :
					ShowScores();
					break;
				default:
					break;
				}
			}

			if(tp.getPressure() > 3) //touch press?
			{
				if((tp.getX() > (lcd.getWidth()-30)) && (tp.getY() < 15))
				{
					TpCalibration(0);
				}
			}
			else
			{
				last_x = 0;
			}
		}
	}
	return 0;
}
